#pragma once
/*
 *  share memory object array
 */
#include "share_mem.h"
#include <map>

namespace anet {
	namespace SShareMemorySpace {
		// where T must has id_type as using id_type = long long or typedef id_type long long.
		template <typename T, size_t Size>
		class CShareMemGlobal {
		public:
			CShareMemGlobal() : m_array(nullptr) {
			}
			virtual ~CShareMemGlobal() = default;
			typedef T value_type;

			// wrapper from value_type, add use flag
			struct wrapperType {
				value_type    value;
				unsigned char flag; // as eShmFlag
				wrapperType() : flag(shm_unuse_flag) {
				}
			};

		public:
			// Create share memory for server.
			bool CreateShm(key_t key) {
				if (!m_shm.CreateShareMem(key, Size * sizeof(wrapperType))) {
					return false;
				}

				m_array = (wrapperType*)m_shm.GetMemory();
				for (int i = 0; i < Size; i++) {
					new(&m_array[i]) wrapperType();
				}
				return true;
			}

			// Open share memory for client
			bool OpenShm(key_t key) {
				if (!m_shm.OpenShareMem(key, Size * sizeof(wrapperType))) {
					return false;
				}
				m_array = static_cast<wrapperType*>(m_shm.GetMemory());
				if (m_array == nullptr) {
					return false;
				}

				// build id_map
				for (size_t i = 0; i < Size; ++i) {
					if (m_array[i].flag == shm_use_flag) {
						m_idMap[m_array[i].value.id] = &m_array[i].value;
					}
				}
				return true;
			}

			// Create object with id.
			value_type* Fetch(typename T::id_type id) {
				auto pTmp = this->GetValue(id);
				if (pTmp != nullptr) {
					return pTmp;
				}

				for (size_t i = 0; i < Size; i++) {
					if (m_array[i].flag == shm_unuse_flag) {
						m_array[i].flag = shm_use_flag;

						// initialize the pValue and return.
						auto pValue = &m_array[i].value;
						pValue->id = id;
						m_idMap[id] = pValue;
						return pValue;
					}
				}
				return nullptr;
			}

			// Release object
			bool Release(value_type *pValue) {
				if (!pValue) {
					return false;
				}

				wrapperType *pShell = (wrapperType*)pValue;
				assert(pShell && "shell object is null");
				if (!pShell) {
					return false;
				}

				assert(pShell->flag == shm_use_flag && "flag is error");
				if (pShell->flag != shm_use_flag) {
					return false;
				}

				assert(pShell >= &m_array[0] && pShell < &m_array[Size]);
				if (!(pShell >= &m_array[0] && pShell < &m_array[Size])) {
					return false;
				}

				pShell->flag = shm_unuse_flag;
				return true;
			}

			// Get free size
			size_t GetFreeSize() {
				size_t count = 0;
				for (size_t i = 0; i < Size; i++) {
					if (m_array[i].flag == shm_unuse_flag) {
						count++;
					}
				}
				return count;
			}

			// Get wrapper_type array.
			wrapperType* GetArray() {
				return m_array;
			}

			// Get value with id.
			value_type* GetValue(typename T::id_type id) {
				auto it = m_idMap.find(id);
				if (it != m_idMap.end()) {
					return it->second;
				} else {
					return nullptr;
				}
			}

		private:
			// share memory object
			CShareMemory m_shm;

			// wrapperType array
			wrapperType *m_array;

			// data map for id to value.
			std::map<typename T::id_type, T*> m_idMap;
		};
	}
}
